﻿/*
具体说来有如下Appender:
AdoNetAppender：利用ADO.NET记录到数据库的日志。
AnsiColorTerminalAppender：在ANSI 窗口终端写下高亮度的日志事件。
AspNetTraceAppender：能用asp.net中Trace的方式查看记录的日志。
BufferingForwardingAppender：在输出到子Appenders之前先缓存日志事件。
ConsoleAppender：将日志输出到控制台。
EventLogAppender：将日志写到Windows Event Log.
FileAppender：将日志写到文件中。
LocalSyslogAppender：将日志写到local syslog service (仅用于UNIX环境下).
MemoryAppender：将日志存到内存缓冲区。
NetSendAppender：将日志输出到Windows Messenger service.这些日志信息将在用户终端的对话框中显示。
RemoteSyslogAppender：通过UDP网络协议将日志写到Remote syslog service。
RemotingAppender：通过.NET Remoting将日志写到远程接收端。
RollingFileAppender：将日志以回滚文件的形式写到文件中。
SmtpAppender：将日志写到邮件中。
TraceAppender：将日志写到.NET trace 系统。
UdpAppender：将日志connectionless UDP datagrams的形式送到远程宿主或以UdpClient的形式广播。
关于使用log4net中可能会使用到的一些参数
%m(message):输出的日志消息，如ILog.Debug(…)输出的一条消息
%n(new line):换行
%d(datetime):输出当前语句运行的时刻
%r(run time):输出程序从运行到执行到当前语句时消耗的毫秒数
%t(thread id):当前语句所在的线程ID
%p(priority): 日志的当前优先级别，即DEBUG、INFO、WARN…等
%c(class):当前日志对象的名称，例如：
%f(file):输出语句所在的文件名。
%l(line)：输出语句所在的行号。
%数字：表示该项的最小长度，如果不够，则用空格填充，如“%-5level”表示level的最小宽度是5个字符，如果实际长度不够5个字符则以空格填充。
下面以一个实际的例子来说明问题，比如在配置中有“%date [%thread] (%file:%line) %-5level %logger [%property{NDC}] - %message%newline”，那么实际的日志中会是如下格式：
“记录时间：2010-11-17 16:16:36,561 线程ID:[9] 日志级别：文件：所在行ERROR 出错类：Log4NetDemo.Program property:[(null)] - 错误描述：error
System.Exception: 在这里发生了一个异常,Error Number:2036084948”
*/
using System;
using System.Linq;
using System.Text;
using Framework.Common.Sql;
using log4net;
using log4net.Core;
using log4net.Repository.Hierarchy;
using log4net.Appender;
using log4net.Layout;
using System.Reflection;
using System.Web;
using Framework.Common.Configs;
using Framework.Serializer;
using Framework.Common.Log;
using Framework.Common.Log.Layout;
using DbType = System.Data.DbType;

namespace Framework.Common
{
    /// <summary>
    /// Log Helper
    /// AdoNetAppender (/configuration/appSettings/add[@"key" ="LogConnectionString"])
    /// 
    /// </summary>
    public static class LogHelper
    {
        static AdoNetAppender adoNetAppender;
        static ILog Instance { get; set; }
        static LogHelper()
        {
            string configFile = CustomConfigRoot.ConfigRootPath + "log4net.config";

            if (FileHelper.FileExists(configFile)) //默认配置文件
            {
                log4net.Config.XmlConfigurator.ConfigureAndWatch(new System.IO.FileInfo(configFile));
                Instance = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
            }
            else //无配置文件
            {
                var hierarchy = (Hierarchy)LogManager.GetRepository();
                hierarchy.Name = "framework.log";

                var patternLayout = new PatternLayout { ConversionPattern = "%d|%-5p|%m%n" };
                patternLayout.ActivateOptions();

                #region RollingFileAppender

                var roller = new RollingFileAppender
                {
                    Layout = patternLayout,
                    AppendToFile = true,
                    RollingStyle = RollingFileAppender.RollingMode.Composite,
                    MaxSizeRollBackups = 4,
                    MaximumFileSize = "1000KB",
                    StaticLogFileName = true,
                    File = PathHelper.MapPath("~/Log/log.txt"),
                    ImmediateFlush = true
                };
                roller.ActivateOptions();
                hierarchy.Root.AddAppender(roller);

                #endregion

                #region ColoredConsoleAppender

                var colorConsole = new ColoredConsoleAppender { Layout = patternLayout };
                colorConsole.AddMapping(new ColoredConsoleAppender.LevelColors
                {
                    BackColor = ColoredConsoleAppender.Colors.Green,
                    ForeColor = ColoredConsoleAppender.Colors.White,
                    Level = Level.Debug,
                });
                colorConsole.AddMapping(new ColoredConsoleAppender.LevelColors
                {
                    BackColor = ColoredConsoleAppender.Colors.Red,
                    ForeColor = ColoredConsoleAppender.Colors.White,
                    Level = Level.Error,
                });
                colorConsole.ActivateOptions();
                hierarchy.Root.AddAppender(colorConsole);

                #endregion

                #region adoAppender

                const string logKey = "Log";
                string logConnStr = SystemConfig.GetConnectionString(logKey);
                if (logConnStr.IsBlank())
                {
                    logConnStr = SystemConfig.GetString(logKey);
                }
                if (logConnStr.IsNotBlank())
                {
                    #region create table

                    try
                    {
                        DbFactory.ByConnectionString(logConnStr).Execute(@"
CREATE TABLE [dbo].[Log](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[Date] [datetime] NOT NULL,
	[Thread] [varchar](255) NOT NULL,
	[Level] [varchar](50) NOT NULL,
	[Message] [varchar](4000) NOT NULL,
	[Exception] [varchar](2000) NULL,
	[ExtLong] [bigint] NULL,
	[ExtString] [varchar](255) NULL,
    CONSTRAINT [PK_Log] PRIMARY KEY CLUSTERED ([Id] ASC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[Log] ADD  CONSTRAINT [DF_Log_Date]  DEFAULT (getdate()) FOR [Date]");
                    }
                    catch
                    {
                    }

                    #endregion

                    adoNetAppender = new AdoNetAppender
                    {
                        CommandText = "INSERT INTO Log ([Date],[Thread],[Level],[Message],[Exception],[ExtLong],[ExtString]) VALUES (@date, @thread, @level, @message, @exception, @extLong, @extString)",
                        ConnectionType = "System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
                        ConnectionString = logConnStr,
                        BufferSize = 5
                    };

                    #region params
                    var paramDate = new AdoNetAppenderParameter
                    {
                        ParameterName = "@date",
                        DbType = DbType.DateTime,
                        Layout = new RawTimeStampLayout()
                    };
                    adoNetAppender.AddParameter(paramDate);
                    var paramThread = new AdoNetAppenderParameter
                    {
                        ParameterName = "@thread",
                        DbType = DbType.String,
                        Size = 255,
                        Layout = GetLayout2RawLayoutAdapter("thread")
                    };
                    adoNetAppender.AddParameter(paramThread);
                    var paramLevel = new AdoNetAppenderParameter
                    {
                        ParameterName = "@level",
                        DbType = DbType.String,
                        Size = 50,
                        Layout = GetLayout2RawLayoutAdapter("level")
                    };
                    adoNetAppender.AddParameter(paramLevel);
                    var paramMessage = new AdoNetAppenderParameter
                    {
                        ParameterName = "@message",
                        DbType = DbType.String,
                        Size = 4000,
                        Layout = GetLayout2RawLayoutAdapter("message")
                    };
                    adoNetAppender.AddParameter(paramMessage);
                    var paramException = new AdoNetAppenderParameter
                    {
                        ParameterName = "@exception",
                        DbType = DbType.String,
                        Size = 2000,
                        Layout = GetLayout2RawLayoutAdapter("exception")
                    };
                    adoNetAppender.AddParameter(paramException);
                    var paramExtLong = new AdoNetAppenderParameter
                    {
                        ParameterName = "@extLong",
                        DbType = DbType.Int64,
                        Layout = GetCustomLayout2RawLayoutAdapter("extLong")
                    };
                    adoNetAppender.AddParameter(paramExtLong);
                    var paramExtString = new AdoNetAppenderParameter
                    {
                        ParameterName = "@extString",
                        DbType = DbType.String,
                        Size = 255,
                        Layout = GetCustomLayout2RawLayoutAdapter("extString")
                    };
                    adoNetAppender.AddParameter(paramExtString);
                    #endregion

                    adoNetAppender.ActivateOptions();
                    hierarchy.Root.AddAppender(adoNetAppender);
                }
                #endregion

                hierarchy.Root.Level = Level.All;
                hierarchy.Configured = true;

                Instance = LogManager.GetLogger(hierarchy.Name);
            }
        }

        /// <summary>
        /// GetLayout2RawLayoutAdapter
        /// </summary>
        /// <param name="pattern"></param>
        /// <returns></returns>
        private static Layout2RawLayoutAdapter GetLayout2RawLayoutAdapter(string pattern)
        {
            var patternLayout = new PatternLayout { ConversionPattern = string.Format("%{0}", pattern) };
            patternLayout.ActivateOptions();
            return new Layout2RawLayoutAdapter(patternLayout);
        }

        /// <summary>
        /// GetLayout2RawLayoutAdapter
        /// </summary>
        /// <param name="pattern"></param>
        /// <returns></returns>
        private static Layout2RawLayoutAdapter GetCustomLayout2RawLayoutAdapter(string pattern)
        {
            var patternLayout = new CustomLayout { ConversionPattern = string.Format("%{0}", pattern) };
            patternLayout.ActivateOptions();
            return new Layout2RawLayoutAdapter(patternLayout);
        }

        #region standard method

        #region Debug
        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        public static void Debug(object message)
        {
            Instance.Debug(message);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="exception"></param>
        public static void Debug(object message, Exception exception)
        {
            Instance.Debug(message, exception);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="extLong"></param>
        /// <param name="extString"></param>
        public static void Debug(object message, long? extLong, string extString)
        {
            var msg = new LogMessage();
            if (message != null)
                msg.Message = message.ToString();
            msg.ExtLong = extLong;
            msg.ExtString = extString;
            Instance.Debug(msg);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="extLong"></param>
        /// <param name="extString"></param>
        /// <param name="exception"></param>
        public static void Debug(object message, long? extLong, string extString, Exception exception)
        {
            var msg = new LogMessage();
            if (message != null)
                msg.Message = message.ToString();
            msg.ExtLong = extLong;
            msg.ExtString = extString;
            Instance.Debug(msg, exception);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="format"></param>
        /// <param name="args"></param>
        public static void DebugFormat(string format, params object[] args)
        {
            string message = string.Format(format, args);
            Instance.Debug(message);
        }
        #endregion

        #region Error
        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        public static void Error(object message)
        {
            Instance.Error(message);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="exception"></param>
        public static void Error(object message, Exception exception)
        {
            Instance.Error(message, exception);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="extLong"></param>
        /// <param name="extString"></param>
        public static void Error(object message, long? extLong, string extString)
        {
            var msg = new LogMessage();
            if (message != null)
                msg.Message = message.ToString();
            msg.ExtLong = extLong;
            msg.ExtString = extString;
            Instance.Error(msg);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="extLong"></param>
        /// <param name="extString"></param>
        /// <param name="exception"></param>
        public static void Error(object message, long? extLong, string extString, Exception exception)
        {
            var msg = new LogMessage();
            if (message != null)
                msg.Message = message.ToString();
            msg.ExtLong = extLong;
            msg.ExtString = extString;
            Instance.Error(msg, exception);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="format"></param>
        /// <param name="args"></param>
        public static void ErrorFormat(string format, params object[] args)
        {
            string message = string.Format(format, args);
            Instance.Error(message);
        }
        #endregion

        #region Fatal
        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        public static void Fatal(object message)
        {
            Instance.Fatal(message);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="exception"></param>
        public static void Fatal(object message, Exception exception)
        {
            Instance.Fatal(message, exception);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="extLong"></param>
        /// <param name="extString"></param>
        public static void Fatal(object message, long? extLong, string extString)
        {
            var msg = new LogMessage();
            if (message != null)
                msg.Message = message.ToString();
            msg.ExtLong = extLong;
            msg.ExtString = extString;
            Instance.Fatal(msg);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="extLong"></param>
        /// <param name="extString"></param>
        /// <param name="exception"></param>
        public static void Fatal(object message, long? extLong, string extString, Exception exception)
        {
            var msg = new LogMessage();
            if (message != null)
                msg.Message = message.ToString();
            msg.ExtLong = extLong;
            msg.ExtString = extString;
            Instance.Fatal(msg, exception);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="format"></param>
        /// <param name="args"></param>
        public static void FatalFormat(string format, params object[] args)
        {
            string message = string.Format(format, args);
            Instance.Fatal(message);
        }
        #endregion

        #region Info
        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        public static void Info(object message)
        {
            Instance.Info(message);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="exception"></param>
        public static void Info(object message, Exception exception)
        {
            Instance.Info(message, exception);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="extLong"></param>
        /// <param name="extString"></param>
        public static void Info(object message, long? extLong, string extString)
        {
            var msg = new LogMessage();
            if (message != null)
                msg.Message = message.ToString();
            msg.ExtLong = extLong;
            msg.ExtString = extString;
            Instance.Info(msg);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="extLong"></param>
        /// <param name="extString"></param>
        /// <param name="exception"></param>
        public static void Info(object message, long? extLong, string extString, Exception exception)
        {
            var msg = new LogMessage();
            if (message != null)
                msg.Message = message.ToString();
            msg.ExtLong = extLong;
            msg.ExtString = extString;
            Instance.Info(msg, exception);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="format"></param>
        /// <param name="args"></param>
        public static void InfoFormat(string format, params object[] args)
        {
            string message = string.Format(format, args);
            Instance.Info(message);
        }
        #endregion

        #region Warn
        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        public static void Warn(object message)
        {
            Instance.Warn(message);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="exception"></param>
        public static void Warn(object message, Exception exception)
        {
            Instance.Warn(message, exception);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="extLong"></param>
        /// <param name="extString"></param>
        public static void Warn(object message, long? extLong, string extString)
        {
            var msg = new LogMessage();
            if (message != null)
                msg.Message = message.ToString();
            msg.ExtLong = extLong;
            msg.ExtString = extString;
            Instance.Warn(msg);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="message"></param>
        /// <param name="extLong"></param>
        /// <param name="extString"></param>
        /// <param name="exception"></param>
        public static void Warn(object message, long? extLong, string extString, Exception exception)
        {
            var msg = new LogMessage();
            if (message != null)
                msg.Message = message.ToString();
            msg.ExtLong = extLong;
            msg.ExtString = extString;
            Instance.Warn(msg, exception);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="format"></param>
        /// <param name="args"></param>
        public static void WarnFormat(string format, params object[] args)
        {
            string message = string.Format(format, args);
            Instance.Warn(message);
        }
        #endregion

        /// <summary>
        /// Flush
        /// </summary>
        public static void Flush()
        {
            if (adoNetAppender != null)
                adoNetAppender.Flush();
        }

        #endregion

        /// <summary>
        /// record web errors log
        /// </summary>
        /// <param name="ex"></param>
        public static void WriteWebErrorDetail(Exception ex)
        {
            var httpEx = ex as HttpException;
            if (httpEx == null || httpEx.ErrorCode == 404 || httpEx.ErrorCode == 403)
            {
                return;
            }
            var httpContext = HttpContext.Current;
            if (httpContext != null)
            {
                var sb = new StringBuilder();
                try
                {
                    var request = httpContext.Request;
                    sb.AppendLine("Error title is :" + ex.Message);
                    sb.AppendLine("Request Url is :" + request.Url);
                    sb.AppendLine("Request UrlReferrer is :" + request.UrlReferrer);
                    sb.AppendLine("------------Request QueryString is :------------");
                    request.QueryString.AllKeys.Each(key => { sb.AppendLine(key + "=" + request.QueryString[key]); });
                    sb.AppendLine("------------Request Forms is :------------------");
                    request.Form.AllKeys.Each(key => { sb.AppendLine(key + "=" + request.Form[key]); });
                }
                catch (Exception newEx)
                {
                    Fatal("AllKeys Error", newEx);
                }
                Fatal(sb.ToString(), ex);
            }
        }

        /// <summary>
        /// Dump To Json Format/PlainText
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="dumpType"></param>
        public static void Dump(Object obj, DumpType dumpType = DumpType.JsonFormat)
        {
            var dumpString = new StringBuilder();
            if (obj == null)
            {
                dumpString.Append("Dumped Object Is Null");
            }
            else
            {
                dumpString.AppendLine(obj.GetType().FullName);
                dumpString.AppendLine();
                string dumpInfo = string.Empty;
                switch (dumpType)
                {
                    case DumpType.JsonFormat:
                        dumpInfo = obj.SToStringJavaScript();
                        break;
                    case DumpType.ObjectDumpFormat:
                        dumpInfo = ObjectDumperHelper.Write(obj, 4).ToString();
                        break;
                }
                dumpString.AppendLine(dumpInfo);
                dumpString.AppendLine();
            }
            Instance.Debug(dumpString);
        }

        public enum DumpType
        {
            JsonFormat,
            ObjectDumpFormat
        }
    }
}
